package com.softaria.windows.logic;

import java.util.HashMap;
import java.util.Set;

import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.softaria.windows.config.MenuConfiguration;
import com.softaria.windows.model.Perspective;
import com.softaria.windows.model.PerspectiveUrl;
import com.softaria.windows.model.View;
import com.softaria.windows.widgets.TabbedWorkPanel;
import com.softaria.windows.widgets.TabbedWorkPanel.Tab;

/**
 * Manages perspectives inside {@link TabbedWorkPanel}. May be considered as
 * business logic on top of {@link TabbedWorkPanel}
 * 
 * @author Roman M Kuzmin (roman@softaria.com)
 * 
 */
class PerspectiveManager {

  /**
   * Passed to openPerspectiveTab and called ONLY in case of creating
   * perspective. If perspective already exists it does not called
   * 
   * @author Roman M Kuzmin (roman@softaria.com)
   * 
   */
  public static interface PerspectiveInitializer {
    void onInitialize(Perspective perspective);
  }

  /**
   * Structure for holding perspective together with its corresponding Tab
   * 
   * @author Roman M Kuzmin (roman@softaria.com)
   * 
   */
  private class PerspectiveAndTab {
    public Perspective perspective;
    public TabbedWorkPanel.Tab tab;

    public PerspectiveAndTab(Perspective perspective, Tab tab) {
      super();
      this.perspective = perspective;
      this.tab = tab;
    }

  }

  private TabbedWorkPanel tabbedWorkPanel;
  private MenuConfiguration menuConf;

  private HashMap<PerspectiveUrl, PerspectiveAndTab> openedPerspectives = new HashMap<PerspectiveUrl, PerspectiveAndTab>();

  /**
   * 
   * @param tabbedWorkPanel
   *          - widget to manage
   * @param menuConf
   *          - used to get label for tab via {@link MenuConfiguration}
   *          .getMenuLabel
   */
  public PerspectiveManager(TabbedWorkPanel tabbedWorkPanel, MenuConfiguration menuConf) {
    this.tabbedWorkPanel = tabbedWorkPanel;
    this.menuConf = menuConf;
  }

  /**
   * Opens or activates (if already exists) perspective. Used by
   * {@link WindowsManager} only.
   * 
   * It can open (create) perspective with ANY perspectiveID. It does not check
   * if it e.g. exists in MenuConfiguration
   * 
   * @param perspectiveId
   * @param closeTabByUserActionListener
   *          - listener to be passed to {@link TabbedWorkPanel} which will be
   *          called when user clicks X button on tab, corresponding to this
   *          perspective
   * @param selectionListener
   *          - listener to be passed to {@link TabbedWorkPanel} which will be
   *          called when user selects tab, corresponding to this perspective
   * @param perspectiveInitializer
   *          - code which should be run only when perspective will be created
   *          (not when it is just activated)
   * @return newly created or activated {@link Perspective}
   */
  Perspective openPerspectiveTab(final PerspectiveUrl perspectiveId,
      ClickHandler closeTabByUserActionListener, Command selectionListener,
      final PerspectiveInitializer perspectiveInitializer) {

    PerspectiveAndTab pat = openedPerspectives.get(perspectiveId);

    if (pat != null) {
      pat.tab.select();
      return pat.perspective;

    } else {
      String name = menuConf.getMenuLabel(perspectiveId);

      if (name == null)
        throw new IllegalArgumentException("Menu config does not know given perspective id: "
            + perspectiveId);

      TabbedWorkPanel.Tab tab = tabbedWorkPanel.createNewTab(name);

      if (closeTabByUserActionListener != null)
        tab.addCloseButtonListener(closeTabByUserActionListener);

      if (selectionListener != null)
        tab.addUserSelectionListener(selectionListener);

      final Perspective perspective = new Perspective();
      perspective.setName(name);
      perspective.setPerspectiveUrl(perspectiveId);
      perspective.setPanel(tab.getPanel());

      pat = new PerspectiveAndTab(perspective, tab);

      openedPerspectives.put(perspectiveId, pat);

      tab.select();

      if (perspectiveInitializer != null)

        perspectiveInitializer.onInitialize(perspective);

      return perspective;
    }

  }

  /**
   * Close perspective with given ID. Called by {@link WindowsManager} only Does
   * nothing about {@link View}s inside this perspective (because it does not
   * even know about them. It is up to {@link WindowsManager} to ensure they are
   * also closed before this call.
   * 
   * @param perspectiveId
   */
  void closePerspectiveTab(PerspectiveUrl perspectiveId) {
    PerspectiveAndTab pat = openedPerspectives.get(perspectiveId);

    if (pat != null) {
      openedPerspectives.remove(perspectiveId);
      pat.tab.delete();

    }
  }

  /**
   * Used only by {@link WindowsManager}
   * 
   * @return list of opened perspective IDs
   */
  Set<PerspectiveUrl> getOpenedPerspectives() {

    return openedPerspectives.keySet();

  }

  /**
   * 
   * @param url
   * @return opened perspective by its URL or null if perspective is not opened
   */
  Perspective getOpenedPerspectivePanel(PerspectiveUrl url) {
    PerspectiveAndTab pat = openedPerspectives.get(url);

    if (pat != null) {
      return pat.perspective;
    } else {
      return null;
    }
  }

}
